package it.unicam.cs.cosy.bioshape.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;

/**
 * Simple component that displays the currently used memory compared to the one
 * that is available at most. The display is being updated every second so you
 * don't need to care about that.
 * 
 * @author Stefan Rybacki
 * 
 */
public final class MemoryMonitor extends JPanel {
	/**
	 * Serialization ID
	 */
	private static final long serialVersionUID = -4735443327535317719L;
	/**
	 * progress bar used for memory consumption visualization
	 */
	private final JProgressBar progressBar;
	/**
	 * garbage collector button
	 */
	private final JButton gcButton;

	@Override
	public Dimension getMaximumSize() {
		Dimension size = getPreferredSize();
		return size;
		// return new Dimension(200, size.height);
	}

	/**
	 * Creates a new memory monitor with or without button for manual garbage
	 * collection
	 * 
	 * @param displayGC
	 *            true if button for garbage collection should be enabled
	 */
	public MemoryMonitor(boolean displayGC) {
		this();
		this.setPreferredSize(new Dimension(200, 20));
		this.setBorder(BorderFactory.createEmptyBorder(-5, 0, 0, 0));
		setDisplayGC(displayGC);
	}

	/**
	 * Creates a new memory monitor display
	 */
	public MemoryMonitor() {
		super(new BorderLayout());
		progressBar = new JProgressBar();
		getProgressBar().setStringPainted(true);

		super.addImpl(getProgressBar(), BorderLayout.CENTER, 0);

		gcButton = new JButton();
		getGcButton().setBorder(new EmptyBorder(2, 2, 2, 2));
		getGcButton().setFocusable(false);
		getGcButton().setFocusPainted(false);
		getGcButton().setContentAreaFilled(false);
		getGcButton().setRolloverEnabled(true);
		getGcButton().setIcon(new ImageIcon(Monitor.class
				.getResource("images/exit162.png")));

		getGcButton().setToolTipText("Run Garbage Collector");
		getGcButton().addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				try {

					getGcButton().setEnabled(false);
					System.gc();
					Thread.sleep(100);
					getGcButton().setEnabled(true);
				} catch (Exception ex) {
					// ignore exception
				}
			}
		});

		super.addImpl(getGcButton(), BorderLayout.EAST, 0);

		// schedule timer to update monitor every second
		Timer timer = new Timer(true);
		timer.scheduleAtFixedRate(new TimerTask() {
			@Override
			public void run() {
				SwingUtilities.invokeLater(new Runnable() {
					@Override
					public void run() {
						// calculate used memory and display in relation to max
						// memory
						final long freeMemory = Runtime.getRuntime()
								.freeMemory();
						final long totalMemory = Runtime.getRuntime()
								.totalMemory();
						final long maxMemory = Runtime.getRuntime().maxMemory();

						String text = String.format(
								"using %s MB of %s MB",
								Long.valueOf((totalMemory - freeMemory) / 1024 / 1024),
								Long.valueOf(maxMemory / 1024 / 1024));
						setToolTipText(text);
						MemoryMonitor.this.getProgressBar()
								.setValue((int) ((totalMemory - freeMemory)
										* MemoryMonitor.this.getProgressBar()
												.getMaximum() / maxMemory));
						MemoryMonitor.this.getProgressBar().setString(text);
						MemoryMonitor.this.getProgressBar().revalidate();
					}
				});
			}

		}, 100, 1000);
	}

	/**
	 * Enables or disables the garbage collection button
	 * 
	 * @param x
	 *            true if manual garbage collection should be allowed
	 */
	public void setDisplayGC(boolean x) {
		getGcButton().setVisible(x);
	}

	@Override
	protected void addImpl(Component comp, Object constraints, int index) {
		throw new UnsupportedOperationException(
				"Can't add components to this component!");
	}

	/**
	 * @return the gcButton
	 */
	public JButton getGcButton() {
		return gcButton;
	}

	/**
	 * @return the progressBar
	 */
	public JProgressBar getProgressBar() {
		return progressBar;
	}
}
